package net.cyndeline.scalarlib.rlgraph.regularEdgeLabeling.altFourCycles

import net.cyndeline.scalarlib.rlgraph.cycles.Cycle
import net.cyndeline.scalarlib.rlgraph.regularEdgeLabeling.angularMap.{AngularVertex, AngularMap}

/**
 * Compares a cycle of vertices to an angular map to see if it constitutes an alternating essential 4-cycle.
 */
class CycleVerifier {

  /**
   * @param c A cycle of length 4 corresponding to a face in an angular map.
   * @param map The angular map of the cycle.
   * @return True if the cycle marks an essential alternating 4-cycle: This is only the case when the cycle corresponds
   *         to a circuit in the angular maps alpha4-orientation. Thus, make sure to test a cycle AND its reverse to
   *         see which one of them (if any) yields true.
   */
  def verifyFaceCycle[V](c: Cycle[AngularVertex[V]], map: AngularMap[V]): Boolean = {
    require(c.length == 4, "Faces in an angular map should all have size 4.")

    // If the cycle isn't a circuit, return false
    for (e <- c.edges) {
      if (map.orientationOfEdge(e._1, e._2) != e._2)
        return false
    }

    true
  }

  /**
   * @param c A directed circuit of length 8 from an angular maps alpha4-orientation.
   * @param map The angular map of the circuit.
   * @return True if the circuit C marks an essential alternating 4-cycle: This is only the case when every black vertex
   *         have no outgoing edges inside C, and every white vertex have their third edge that isn't on C inside C.
   *         The cycle is considered to be moving clockwise, so "inside" refers to clockwise of the outgoing edge of a
   *         vertex, and counter clockwise of the incoming edge on C. Thus, make sure to test a cycle AND its reverse to
   *         see which one of them (if any) yields true.
   */
  def verifyEightCycle[V](c: Cycle[AngularVertex[V]], map: AngularMap[V]): Boolean = {
    require(c.length == 8, "Alternating 4-cycles that doesn't correspond to a face in the angular map must have size 8.")
    val edgePairs = c.edges zip (c.edges.drop(1) :+ c.edges.head)

    for (edges <- edgePairs) {
      val currentVertex: AngularVertex[V] = edges._1._2
      val nextVertex = edges._2._2
      val previousVertex = edges._1._1
      var currentNeighbor = map.angularEmbedding
        .embeddingFor(currentVertex)
        .entryFor(nextVertex)
        .next

      // Black case
      if (currentVertex.isBlack) {
        while (currentNeighbor.adjacentVertex != previousVertex) {
          if (map.orientationOfEdge(currentVertex, currentNeighbor.adjacentVertex) == currentNeighbor.adjacentVertex)
            return false

          currentNeighbor = currentNeighbor.next
        }

      } else { // White case

        /* If the next clockwise neighbor of this white vertex is the previous vertex, that means that the third
         * white vertex is outside C, since every white vertex only has three neighbors.
         */
        if (currentNeighbor.adjacentVertex == previousVertex)
          return false

      }
    }

    true
  }

}
